package com.gl.basis.zuul.config;

import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.util.StringUtils;

import com.gl.basis.zuul.enums.RouteType;
import com.gl.basis.zuul.outdto.Route;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 路由定位器
 *
 */
public class RouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    // region 私有变量
    private ZuulProperties properties;

    private ZuulConfig zuulConfig;

    private List<Route> list;

    private boolean dirty = true;

    private Map<String, ZuulProperties.ZuulRoute> routesMap;
    // endregion

    // region 公共方法
    public RouteLocator(String servletPath, ZuulProperties properties, ZuulConfig zuulConfig) {
        super(servletPath, properties);
        this.properties = properties;
        this.zuulConfig = zuulConfig;
        this.list = new ArrayList<>();
    }

    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        if (dirty) {
            //从db中加载路由信息
            Map<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>(this.locateRoutesFromDB());
            //从application.properties中加载路由信息
            Map<String, ZuulProperties.ZuulRoute> ymlRoutes = super.locateRoutes();
            ymlRoutes.forEach(routesMap::putIfAbsent);
            //优化一下配置
            this.routesMap = this.optimize(routesMap);
            this.dirty = false;
        }
        return routesMap;
    }

    public void setDirty(boolean dirty, List<Route> list) {
        this.dirty = dirty;
        this.list = list;
    }
    // endregion

    // region 私有方法
    private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromDB() {
        convertBlack(list);
        convertWhite(list);
        return list.stream()
            .filter(o -> RouteType.ROUTE.getCode().equals(o.getRouteType()))
            .collect(Collectors.toMap(Route::getPath, this::convert, (k1, k2) -> k1));
    }

    private Map<String, ZuulProperties.ZuulRoute> optimize(Map<String, ZuulProperties.ZuulRoute> routesMap) {
        Map<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }

    private List<String> convertBlack(List<Route> list) {
        List<String> blackList = list.stream()
            .filter(o -> RouteType.BLACK.getCode().equals(o.getRouteType()))
            .map(Route::getPath)
            .collect(Collectors.toList());
        zuulConfig.setBlackListExtenal(blackList);
        return blackList;
    }

    private List<String> convertWhite(List<Route> list) {
        List<String> whiteList = list.stream()
            .filter(o -> RouteType.WHITE.getCode().equals(o.getRouteType()))
            .map(Route::getPath)
            .collect(Collectors.toList());
        zuulConfig.setWhiteListExtenal(whiteList);
        return whiteList;
    }

    private ZuulProperties.ZuulRoute convert(Route route) {
        ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
        zuulRoute.setId(route.getRouteId());
        zuulRoute.setPath(route.getPath());
        zuulRoute.setServiceId(route.getServiceId());
        return zuulRoute;
    }
    // endregion
}
